描述:可以捕获和存储其所在上下文中任意常量或变量的引用的字包含的函数代码块。
说明:由3
种类型的闭包。
分类 | 是否有名字 | 捕获内容 | 适用 |
---|---|---|---|
全局函数 | 是 | 无 | 严格说来不是闭包 |
嵌套函数 | 是 | 其封闭函数域内的变量或常量的引用 | 在较复杂函数中,需要命名和定义自包含代码模块的场景 |
闭合表达式 | 否 | 其上下文中的变量或常量的引用 | 处理一些函数并需要将另外一些函数作为该函数的参数时 |
7.1 闭包表达式
描述:闭包表达式是一种利用简洁语法构建内联闭包的方式
说明:和全局函数
或嵌套函数
相比有如下特点
- 类型推断:利用上下文推断
参数
和返回值
类型- 隐式返回:隐式返回单表达式闭包,即单表达式闭包可以省略
return
语句。- 参数名称缩写
- 尾随闭包语法
语法:一般形式
1 | { (parameters) -> returnType in |
- 可以使用
常量
、变量
和inout
类型作为参数- 参数不支持设置
默认值
- 可以在参数列表的最后使用
可变参数
元组
也可以作为参数和返回值
1 | let names = ["Charis", "Alex", "Ewa", "Barry", "Daniella"] |
7.2 尾随闭包
条件:当将闭包作为最后一个参数传递给函数时
用途:增强函数的可读性
(其实就是一种特殊条件下的简写)
语法:书写在函数()
之后,用{}
括起来。
1 | /* 案例1:基本案例 */ |
7.3 捕获值
说明:能够捕获值的闭包有
2
种
- 嵌套函数:捕获其外部函数所有的参数以及定义的常量和变量
- 闭包表达式:闭包可以在其被定义的上下文中捕获常量或变量
注意:
- 如果一个值是不可变的,Swift 可能会改为捕获并保存一份对值的拷贝
- Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量
1 | // 捕获值 |
7.4 闭包是引用类型
说明:这意味着不同的变量或常量可以引用同一个闭包。
7.5 逃逸闭包和非逃逸闭包
逃逸:当一个闭包作为参数传到一个函数中,但是这个闭包在函数执行过程中没有执行,函数返回之后才被执行或者从来没有执行过,我们称该闭包从函数中逃逸。
说明:通过标注传入闭包的闭包为@noescape
,让编译器对非逃逸闭包
进行优化。
@noescape
说明:告诉编译器,闭包必须在被传入到的函数体中执行。
- 将闭包标注为
@noescape
使你能在闭包中隐式地引用self
(因为@noescape
保障了成员方法)用途:使编译器能够进行一些更加激进的优化。
注意:如果被标注了@noescape
的闭包没有在函数体中执行,编译器讲报错。
1 |
|
7.6 自动闭包
描述:
自动闭包
是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。
限制:
用途:这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的{}
。
说明:有2
种方式
- 显式创建:
{语句}
技巧:自动闭包让你能够
延迟求值
,因为代码段不会被执行直到你调用这个闭包。延迟求值对于那些有副作用
和代价昂贵
的代码来说是很有益处的,因为你能控制代码什么时候执行。
7.6.1 通过普通闭包实现延迟执行
案例1:先创建闭包然后调用
1 | var customersInLine = ["Chris", "Aelx", "Ewa", "Barry", "Daniella"] |
案例2:将闭包作为参数传递
1 | func serveCustomer(customerProvider: () -> String) { |
7.6.2 自动闭包实现延迟执行
@autoclosure和@autoclosure(escaping)
说明:通过将参数标记为
@autoclosure
来接收一个自动闭包。
分类 | 是否允许闭包逃逸 | 备注 |
---|---|---|
@autoclosure | 不 | 暗含了@noescape 特性 |
@autoclosure(escaping) | 是 |
@autoclosure
1 | /* 通过autoclosure标记,自动将传入的参数(语句)转换为闭包 */ |
@autoclosure(escaping)
1 | var customersInLine = ["Barry", "Daniella"] |